package com.library.application;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.library.domain.Book;
import com.library.infrastructure.persistence.JpaBookRepository;
import com.library.infrastructure.MinioService;
import com.library.infrastructure.CoverGeneratorService;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Service
public class BookService {
    private static final Logger logger = LoggerFactory.getLogger(BookService.class);

    private final JpaBookRepository bookRepository;
    private final MinioService minioService;
    private final CoverGeneratorService coverGeneratorService;

    @Autowired
    public BookService(JpaBookRepository bookRepository, MinioService minioService, CoverGeneratorService coverGeneratorService) {
        this.bookRepository = bookRepository;
        this.minioService = minioService;
        this.coverGeneratorService = coverGeneratorService;
    }

    public Page<Book> getAllBooks(Pageable pageable) {
        Page<Book> books = bookRepository.findAll(pageable);
        books.forEach(this::setCoverUrl);
        return books;
    }

    @Transactional
    public Book addBook(Book book, MultipartFile cover) throws Exception {
        try {
            String fileName;
            if (cover != null && !cover.isEmpty()) {
                fileName = UUID.randomUUID().toString() + "_" + cover.getOriginalFilename();
                String coverUrl = minioService.uploadFile(cover, fileName);
                if (coverUrl.length() > 2000) {
                    throw new IllegalArgumentException("Cover URL is too long");
                }
                book.setCoverUrl(coverUrl);
            } else {
                InputStream generatedCover = coverGeneratorService.generateCover(book.getTitle(), book.getAuthor());
                fileName = UUID.randomUUID().toString() + "_generated_cover.png";
                String coverUrl = minioService.uploadInputStream(generatedCover, fileName, "image/png");
                if (coverUrl.length() > 2000) {
                    throw new IllegalArgumentException("Cover URL is too long");
                }
                book.setCoverUrl(coverUrl);
            }
            return bookRepository.save(book);
        } catch (Exception e) {
            logger.error("Error adding book: {}", book, e);
            throw new RuntimeException("Failed to add book", e);
        }
    }

    public Book borrowBook(UUID id) {
        Book book = getBookById(id);
        book.borrow();
        return bookRepository.save(book);
    }

    public Book returnBook(UUID id) {
        Book book = getBookById(id);
        book.returnBook();
        return bookRepository.save(book);
    }

    public InputStream getBookCover(UUID id) throws Exception {
        Book book = getBookById(id);
        String fileName = book.getCoverUrl().substring(book.getCoverUrl().lastIndexOf("/") + 1);
        return minioService.downloadFile(fileName);
    }

    public Book getBookById(UUID id) {
        try {
            Book book = bookRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Invalid book ID: " + id));
            setCoverUrl(book);
            logger.info("Retrieved book: {}", book);
            return book;
        } catch (IllegalArgumentException e) {
            logger.error("Error getting book by ID: {}", id, e);
            throw e;
        }
    }

    public Page<Book> searchBooks(String keyword, Pageable pageable) {
        Page<Book> books;
        if (keyword == null || keyword.trim().isEmpty()) {
            books = bookRepository.findAll(pageable);
        } else {
            books = bookRepository.findByTitleOrAuthorContainingIgnoreCase(keyword.trim(), pageable);
        }
        books.forEach(this::setCoverUrl);
        return books;
    }

    private void setCoverUrl(Book book) {
        if (book.getCoverUrl() != null) {
            try {
                String presignedUrl = minioService.getPresignedUrl(book.getCoverUrl());
                book.setCoverUrl(presignedUrl);
            } catch (Exception e) {
                logger.error("Error getting presigned URL for book cover: {}", book.getId(), e);
            }
        }
    }
}